﻿using System.Collections.Concurrent;
using SysLog;

namespace PLCComponent;

using System;
using System.Threading;
using S7.Net;

public class PlcValueMonitor
{

    private static ConcurrentDictionary<string, PlcValueMonitor> _monitors = new();
    private object _lastValue;
    private readonly DataType _dataType;
    private readonly string _plcName;
    private bool _isMonitoring;
    private readonly VarType _varType;
    private readonly PlcConnectionPool _pool;
    private readonly string _variableAddress;
    private Timer _pollingTimer;
    private readonly int _pollingInterval;
    private string _monitorID;
    
    /// <summary>
    /// 当PLC变量值改变时触发
    /// </summary>
    public event Action<object> OnValueChanged;

    /**
     *  plcName plc名称
     *  variableAddress db单元地址
     *  varType 数据类型
     *  pollingInterval 轮询间隔时间
     */
    public PlcValueMonitor(PlcConnectionPool pool,string plcName, VarType varType,string variableAddress, int pollingInterval)
    {
        // 获取PLC连接
        _plcName = plcName;
        _pool = pool;
        _variableAddress = variableAddress;
        _varType = varType;
        _pollingInterval = pollingInterval;
        _monitorID = (_plcName + _variableAddress).GetHashCode().ToString();
    }

    private void PollCallback(object state)
    {
        try
        {
            // 读取PLC数据
            object currentValue = ReadValue();
            // 比较值是否变化
            if (!Equals(currentValue, _lastValue))
            {
                _lastValue = currentValue;
                OnValueChanged?.Invoke(currentValue);
            }
        }
        catch (Exception ex)
        {
            ILog.writeError($"Polling error: {ex.Message}");
        }
    }

    private object ReadValue()
    {
        switch (_varType)
        {
            case VarType.Bit:
                return _pool.ReadBit(_plcName, _variableAddress);
            case VarType.Byte:
                return _pool.ReadByte(_plcName, _variableAddress);
            case VarType.Int:
                return _pool.ReadInt(_plcName, _variableAddress);
            case VarType.DWord:
                return _pool.ReadDWord(_plcName, _variableAddress);
            case VarType.String:
                return _pool.ReadString(_plcName, _variableAddress);
            case VarType.Real:
                return _pool.ReadReal(_plcName, _variableAddress);
            default:
                throw new NotSupportedException($"Type {_varType} not supported");
        }
    }

    public static PlcValueMonitor Monitor(string plcName, string variableAddress)
    {
        string monitorID = (plcName + variableAddress).GetHashCode().ToString();
        if (_monitors.ContainsKey(monitorID))
        {
            return _monitors[monitorID];
        }
        return null;
    }
    
    public void Start()
    {
        if (_isMonitoring) return;
        _isMonitoring = true;
        //初始化监听数据
        _lastValue = ReadValue();
        _pollingTimer = new Timer(PollCallback, null, 0, _pollingInterval);
        if (! _monitors.ContainsKey(_monitorID))
        {
            _monitors.TryAdd(_monitorID, this);
        }
        ILog.writeInfo($"PLC监控已启动，轮询间隔: {_pollingInterval}ms");
    }


    public void Stop()
    {
        _pollingTimer?.Dispose();
        if (_monitors.ContainsKey(_monitorID))
        {
            _monitors.TryRemove(_monitorID, out var plcValueMonitor);
        }
    }
}

public enum VarType { Bit, Byte, Int, DWord, Real ,Double,String }